﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Rtc.Signaling;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.Presence;
using System.IO;
using System.Xml;
using System.Configuration;
using System.Net;
using System.Threading;

namespace WFBot.Rtc
{
    public class PresenceUpdater
    {
        private readonly List<string> _targets;
        private readonly List<string> _availableOps;
        private CollaborationPlatform _collaborationPlatform;
        private UserEndpoint _userEndpoint;
        private AutoResetEvent autoReset = new AutoResetEvent(false);
        private RemotePresence _remotePresence;
        private bool _initialized;
        private object _sync = new object();

        public PresenceUpdater()
        {
            _targets = new List<string>();
            _availableOps = new List<string>();
        }

        public void AddTarget(string sip)
        {
            if (!_targets.Contains(sip))
                _targets.Add(sip);
        }

        public bool IsAvailable(string sip)
        {
            return _availableOps.Any(o => o == sip);
        }

        public List<string> GetAvailableOps()
        {
            return _availableOps;
        }

        public void Initialize()
        {
            if (_initialized) return;

            lock (_sync)
            {
                if (_initialized) return;

                InitializePlatform("wfbotpresenceupdater");

                string userName = ConfigurationManager.AppSettings["userName"];
                string password = ConfigurationManager.AppSettings["password"];
                string domain = ConfigurationManager.AppSettings["domain"];
                string uri = ConfigurationManager.AppSettings["uri"];
                string server = ConfigurationManager.AppSettings["server"];

                InitializeUserEndPoint(userName, password, domain, uri, server);

                _initialized = true;
            }
        }

        private void InitializePlatform(string applicationName)
        {
            ClientPlatformSettings clientPlatformSettings = new ClientPlatformSettings(applicationName, SipTransportType.Tls);
            clientPlatformSettings.DefaultAudioVideoProviderEnabled = false;
            _collaborationPlatform = new CollaborationPlatform(clientPlatformSettings);
            _collaborationPlatform.BeginStartup(EndPlatformStartup, null);

            autoReset.WaitOne();
        }

        private void ShutdownPlatform()
        {
            _collaborationPlatform.BeginShutdown(PlatformShutdownCompleted, null);
        }

        private void PlatformShutdownCompleted(IAsyncResult result)
        {
            _collaborationPlatform.EndShutdown(result);
        }

        private void InitializeUserEndPoint(string userName, string password, string domain, string uri, string server)
        {
            NetworkCredential credential = new NetworkCredential(userName, password, domain);
            UserEndpointSettings userEndpointSettings = new UserEndpointSettings(uri, server);
            userEndpointSettings.Credential = credential;
            _userEndpoint = new UserEndpoint(_collaborationPlatform, userEndpointSettings);
            _userEndpoint.BeginEstablish(EndEndpointEstablish, _userEndpoint);

            autoReset.WaitOne();
        }

        private void EndPlatformStartup(IAsyncResult ar)
        {
            try
            {
                _collaborationPlatform.EndStartup(ar);
            }
            catch (ConnectionFailureException connFailEx)
            {
                Console.WriteLine(connFailEx.ToString());
            }
            finally
            {
                autoReset.Set();
            }
        }

        private void EndEndpointEstablish(IAsyncResult ar)
        {
            try
            {
                _userEndpoint.EndEstablish(ar);

                _remotePresence = _userEndpoint.RemotePresence;

                _remotePresence.PresenceNotificationReceived += new EventHandler<RemotePresenceNotificationEventArgs>(RemotePresence_PresenceNotificationReceived);

                foreach (var remoteUser in _targets)
                {
                    RemotePresentitySubscriptionTarget _target;
                    _target = new RemotePresentitySubscriptionTarget(new RealTimeAddress(remoteUser), null);
                    _remotePresence.BeginAddTargets(new RemotePresentitySubscriptionTarget[] { _target }, AddTargetsCompleted, null);
                }
            }
            catch (ConnectionFailureException connFailEx)
            {
                Console.WriteLine(connFailEx.ToString());
            }
            catch (InvalidOperationException iOpEx)
            {
                Console.WriteLine(iOpEx.ToString());
            }
            catch (RegisterException regEx)
            {
                Console.WriteLine(regEx.ToString());
            }
            finally
            {
                autoReset.Set();
            }
        }

        private void AddTargetsCompleted(IAsyncResult result)
        {
            try
            {
                _remotePresence.EndAddTargets(result);
            }
            catch (PublishSubscribeException pse)
            {
                throw;
            }
            catch (RealTimeException rte)
            {
                throw;
            }
        }


        void RemotePresence_PresenceNotificationReceived(object sender, RemotePresenceNotificationEventArgs e)
        {
            foreach (RemotePresentityNotificationData notification in e.Notifications)
            {
                foreach (PresenceCategoryWithMetaData category in notification.Categories)
                {
                    if (category.Name.Equals("state"))
                    {
                        //get the xml data
                        string rawXml = category.CreateInnerDataXml();
                        if (rawXml == null || rawXml.Trim().Length == 0)
                        {
                            break;
                        }

                        StringReader reader = new StringReader(rawXml);
                        XmlDocument metadataDocument = new XmlDocument();
                        metadataDocument.Load(reader);

                        XmlNodeList stateNodeList = metadataDocument.ChildNodes;
                        if (stateNodeList == null || stateNodeList.Count == 0)
                        {
                            continue;
                        }

                        //find the availability value

                        XmlNode stateNode = stateNodeList[0];

                        if (stateNode.Name != "state")
                        {
                            continue;
                        }

                        XmlNodeList availabilityNodeList = stateNode.ChildNodes;
                        if (availabilityNodeList == null || availabilityNodeList.Count == 0)
                        {
                            continue;
                        }

                        XmlNode availabilityNode = availabilityNodeList[0];
                        if (availabilityNode.Name != "availability")
                        {
                            continue;
                        }

                        if (availabilityNode.InnerText == null)
                        {
                            continue;
                        }

                        int availability;

                        if (Int32.TryParse(availabilityNode.InnerText, out availability))
                        {
                            if (availability == 3500)
                            {
                                if (!_availableOps.Contains(notification.Uri))
                                {
                                    _availableOps.Add(notification.Uri);
                                }
                            }
                            else
                            {
                                if (_availableOps.Contains(notification.Uri))
                                {
                                    _availableOps.Remove(notification.Uri);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
